home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ShareWare OnLine 2
/
ShareWare OnLine Volume 2 (CMS Software)(1993).iso
/
infor
/
ddj_more.zip
/
MORE.C
next >
Wrap
Text File
|
1993-01-04
|
17KB
|
631 lines
#include <bios.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <process.h>
/* MORE.C Page input to stdout.
*
* (C) 1986, Allen I. Holub. All rights reserved.
*
* Usage: more [-<offset>] file...
*
* Exit status: 0 always
*/
/*------------------------------------------------------------------------*/
#define b_getc() bioskey(0)
#define look() bioskey(1)
/*------------------------------------------------------------------------*/
#define CAN 0x18 /* ^X */
#define ESC 0x1b /* ^[ */
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
#define BSIZE 256 /* Maximum length of a line in the file */
#define PAGESIZE 23 /* # of lines to output before stopping */
#define E(x) fprintf(stderr,"%s\n",x)
#define HAS_DOT(p) strchr(p,'.')
FILE *Ifile = stdin; /* Current input file */
char *Ifile_name = "/dev/con"; /* Name of file associated w/ Ifile */
int Repeat_count = 1; /* Repeat count for most recent cmd */
long Line = 0; /* # of output lines printed so far */
long Flen = 0; /* Length of input file in chars */
long Start_here = 0; /* Seek to here when prog starts */
/*--------------------------------------------------------------------------
* Stack used to keep track of start of line. Maximum number
* of lines is determined by STACKSIZE.
*/
typedef long STACKTYPE;
#define STACKSIZE (1024*6) /* Must be divisible by 2 */
STACKTYPE Stack[ STACKSIZE ];
STACKTYPE *Sp = Stack + STACKSIZE;
#define STACKFULL (Sp <= Stack)
#define STACKEMPTY (Sp >= Stack + STACKSIZE)
#define CLEAR_STACK() Sp = Stack + STACKSIZE ;
#define TOS (STACKEMPTY ? 0 : *Sp)
#define BACK_SCRN *( min( Sp+(PAGESIZE-1), Stack+(STACKSIZE-1)) )
#define erase_line() line( ' ', 0 ) /* Draw a line of spaces */
/*--------------------------------------------------------------------------*/
help()
{
register int i;
/* Print a help message with a box around it, special IBM
* graphics characters are used for the box.
*/
putc( 0xd6, stderr );
for( i=56 ; --i >= 0 ; putc( 0xc4, stderr) )
;
E("\267");
E("\272 b ............... go (B)ack a page \272");
E("\272 e ............... go to end of file \272");
E("\272 n ............... go to (N)ext file \272");
E("\272 o ............... print (O)ffset from start of file \272");
E("\272 q ............... (Q)uit (return to DOS) \272");
E("\272 s ............... (S)kip one line (w/o printing) \272");
E("\272 r ............... (R)ewind file (go back to begining) \272");
E("\272 ! ............... execute a program (type blank line \272");
E("\272 at prompt to execute last) \272");
E("\272 / ............... search for regular expression \272");
E("\272 (type blank line at prompt for last) \272");
E("\272 ESC ............. Scroll until any key is hit \272");
E("\272 CR .............. next line \272");
E("\272 SP .............. next screen \272");
E("\272 anything else ... print this list \272");
E("\272 \272");
E("\272 All commands may be preceeded by a count. \272");
putc( 0xd3, stderr );
for( i=56 ; --i >= 0 ; putc( 0xc4, stderr ) )
;
E("\275");
}
/*--------------------------------------------------------------------------*/
usage()
{
E("more: Copyright (C) 1986, Allen I. Holub. All rights reserved.");
E("\nUsage: more [+<num>] [file...] \n");
E("Print all files in list on the screen, pausing every 23 lines");
E("If + is specified, more will start printing at character <num>");
E("One of the following commands is executed after each page:");
help();
exit(1);
}
/*--------------------------------------------------------------------------*/
push( long file_posn ) /* Push file_posn onto the stack */
{
if( STACKFULL ) /* If stack is full, compress it */
comp_stk();
*( --Sp ) = file_posn;
}
/*--------------------------------------------------------------------------*/
long pop()
{
/* Pop one entry off the stack and return the file
* position
*/
return STACKEMPTY ? 0 : *Sp++ ;
}
/*--------------------------------------------------------------------------*/
comp_stk()
{
/* Compress the stack by removing every other entry.
* This routine is called when the stack is full (we've
* read more lines than the stack can hold).
*/
register STACKTYPE *dest, *src;
fprintf(stderr,"\007Stack Full: Compressing\n");
src = dest = Stack + STACKSIZE;
while( (src -= 2) >= Stack )
*--dest = *src;
Sp = dest;
}
/*--------------------------------------------------------------------------*/
getcon()
{
/* Get one character from the console using a direct
* BIOS call. Map \r into \n if one is encountered.
*/
register int c;
c = b_getc() & 0x7f; /* Get a character from console */
putchar(c); /* Echo character */
return (c == '\r' ) ? '\n' : c ;
}
/*--------------------------------------------------------------------------*/
clear_io()
{
/* Clears the entire I/O queue, both at the BIOS and the
* BDOS level.
*/
while( look() )
b_getc();
#ifdef NEVER
while( kbhit() )
getchar();
#endif
}
/*--------------------------------------------------------------------------*/
khit() /* Return true if a key has been hit on the */
{ /* physical keyboard (as compared with a */
if( look() ) /* character available on stdin). */
{
clear_io();
return 1;
}
return 0;
}
/*--------------------------------------------------------------------------*/
char *getbuf( register char *p )
{
/* Get a line of input using direct console I/O and put it
* into buf. Return a pointer to the first whitespace on the
* line or to the end of line if none. This routine is for
* getting commands from the user, not for getting normal
* input. ^H is supported as a destructive backspace but no
* other editing is available.
*/
register int c;
int gottail = 0;
char *start = p;
char *tail = "";
clear_io();
while( (c=getcon()) != '\n' )
{
if( c == '\b' )
{
if( p <= start )
fputs( "!\007", stderr );
else
{
--p;
fputs( " \b", stderr );
}
}
else
{
if( isspace(c) && !gottail )
gottail = (int)( tail = p );
*p++ = c;
}
}
*p = '\0';
return( p <= start ? NULL : tail );
}
/*--------------------------------------------------------------------------*/
percent( s )
char *s;
{
/* Print the percentage of the file that we've seen so far */
printf("%4.1f%%%s", ((double)TOS / (double)Flen) * 100.00, s );
}
/*--------------------------------------------------------------------------*/
int getcmd()
{
/* Get a command from the keyboard, using direct
* bios I/O. Commands take the form [num]<c>. Returns
* the command. Repeat_count is initialized to hold [num]
* or 1 if no num is entered.
*/
int c;
clear_io();
percent("");
printf(", line %ld (? for commands): ", Line );
Repeat_count = 0;
while( '0' <= (c=getcon()) && c <= '9' )
Repeat_count = (Repeat_count * 10) + (c - '0');
if( Repeat_count == 0 )
Repeat_count = 1;
erase_line();
if( c == 0x03 ) /* ^C == abort */
exit(1);
return( c );
}
/*--------------------------------------------------------------------------*/
char *inputline( suppress )
{
/* Get a line from the file being processed and put it into
* buf. Push the start of line character onto the stack.
* return 0 on end of file, a pointer to the line (i.e. to buf)
* otherwise.
*/
register int rval;
register long start_of_line;
static char buf[BSIZE];
start_of_line = ftell( Ifile );
if( rval = (int) fgets(buf, BSIZE, Ifile) )
{
Line++;
push( start_of_line );
if( !suppress )
fputs( buf, stdout );
}
return rval ? buf : NULL ;
}
/*--------------------------------------------------------------------------*/
printpage()
{
/* Print an entire page from the input file */
register int i;
for( i=PAGESIZE-1 ; --i>=0 && inputline(0) ; )
;
}
/*--------------------------------------------------------------------------*/
search()
{
/* Prompt for a pattern and then search for it in the
* file. Stop searching if the pattern is found or if
* any key is hit. The previous pattern is remembered
* in a local array so, if CR is entered instead of a
* pattern, the previous pattern is used.
*/
static char pat[128], opat[128];
char *iline;
extern int *makepat();
extern char *matchs();
int *template;
printf("/");
if( !getbuf( pat ) )
strcpy( pat, opat );
if( !(template = makepat( pat, 0 )) )
printf("Illegal regular expression: %s\n", pat );
else
{
erase_line();
printf( "/%s\n", pat );
while( (iline = inputline(1)) && !khit() )
{
percent("\r");
if( matchs( iline, template, 0 ) )
break;
}
unmakepat( template );
fseek( Ifile, pop(), 0 ); /* Back up one line */
--Line;
line( 0xcd, 1 );
printpage();
}
strcpy( opat, pat );
}
/*--------------------------------------------------------------------------*/
execute()
{
/* Spawn off a child process. When the process terminates
* print a message and redraw the current page. Note that
* spawn() is used (rather than system()) so you can't
* execute a batch file or a built in command. This
* subruotine will set the CMDLINE environment variable
* to a null string for the sake of those routines that
* are executing under the shell which will use it.
*/
static char buf[128];
char *tail = " ";
static char obuf[128], *otail = obuf;
register char *p;
register int c;
printf("!");
if( !(tail = getbuf(buf)) ) /* If no command entered, */
{ /* use the same one we */
tail = otail; /* used last time. */
memcpy( buf, obuf, 128 );
printf( "\n!%s\n", buf );
}
#ifdef NEVER
else
{
if( *tail )
*tail++ = '\0';
}
if( HAS_DOT(buf) )
{
/* Spawnlp will actually try to execute any file that you
* give it. If you say to execute an ASCII file, it will
* load that file into memory, try to execute it, and die
* a horrible death. We attempt to avoid this by checking
* for a dot in the file name. You may want to put a more
* rigorous test here.
*/
fprintf(stderr, "\007<%s> is not a command\n", buf);
}
else
{
putenv("CMDLINE=");
if( spawnlp(P_WAIT, buf, buf, tail, NULL) == -1 )
fprintf(stderr, "Can't execute <%s %s>\n", buf, tail );
}
#endif
putenv("CMDLINE=");
if( system( buf ) )
fprintf(stderr, "Can't execute <%s>\n", buf );
printf("Hit any key to continue ....");
getcon();
erase_line();
putchar('\n');
otail = tail;
memcpy( obuf, buf, 128 );
}
/*--------------------------------------------------------------------------*/
line( c, newline )
{
/* Print a line of characters to mark top of page. 0xcd
* is the IBM graphics character for a horizontal double
* line. The cursor is put at the begining of next line
* if "newline" is true, else it's put at begining of
* current line.
*/
register int i;
putchar('\r');
for( i = 79 ; --i >= 0 ; putchar( c ) )
;
putchar( newline ? '\n' : '\r' );
}
/*--------------------------------------------------------------------------*/
backapage( count )
{
/* Go back count pages and print the resulting page. */
register int i;
i = ((count+1) * PAGESIZE) - 3;
while( --i >= 0 )
{
Line--;
pop();
}
line( 0xcd, 1 );
fseek( Ifile, pop(), 0 );
Line = max( Line - 1, 0 );
printpage();
}
/*--------------------------------------------------------------------------*/
docmd( cmd, ateof )
{
/* Do a single command, return 1 if next file is requested.
* Actually call exit on a "quit" command or ^C.
*/
register int rval = 0;
register int i;
long posn;
do{
switch( cmd )
{
case CAN: break; /* NOP */
case 'q': exit(0); /* abort */
case '\n': if( ateof ) /* Forward motion */
rval = 1; /* one line */
else
inputline(0);
break;
case ' ': if( ateof ) /* one page */
rval = 1;
printpage();
break;
case 'e': erase_line(); /* To end of file */
while( inputline(1) && !khit() )
percent("\r");
break;
case 's': if( ateof ) /* one line w/o printing */
rval=1;
else
{
erase_line();
inputline(1);
percent("\r");
}
break;
case ESC: if( ateof ) /* scroll till key is hit */
rval = 1;
else
while( inputline(0) && !khit() )
clear_io();
clear_io();
Repeat_count = 0; /* Ignore repeat count */
break; /* if it's set. */
case 'n': rval = 1; /* to next file */
break;
case '/': search(); /* search for pattern */
break;
case 'r': line( 0xcd, 1 ); /* to start of file */
CLEAR_STACK();
Line = 0;
fseek( Ifile, 0L, 0 );
printpage();
break;
case 'b': backapage( Repeat_count ); /* to a previous page */
Repeat_count = 0;
break;
case 'o': printf("Top line = %ld, ", BACK_SCRN ); /* print file */
printf("Bottom line = %ld\n", TOS); /* position */
break;
case '!': /* Close the file and spawn another shell.
* When we come back, reopen the file and
* position to the same place we were before.
* This is necessary because of a bug in
* Microsoft C ver. 3.0's spawn functions
* (they trash the IOB). It will cause
* problems if standard input is used as
* the input source (as in a pipe) because
* we won't be able to successfully reopen
* stdin.
*/
Repeat_count = 0; /* Ignore repeat count */
/* fclose( Ifile );*/
execute();
posn = pop();
/* if( Ifile = fopen(Ifile_name, "r") )
{
fseek( Ifile, posn, 0 );*/
backapage( 0 );
/* }
else
{
fprintf(stderr,"more: can't open %s\n",
Ifile_name);
rval = 1;
}*/
break;
default: help(); /* Print the help message */
cmd = getcmd(); /* get a new command */
Repeat_count++;
break;
}
} while( --Repeat_count > 0 );
return( rval );
}
/*--------------------------------------------------------------------------*/
dofile( char *fname )
{
/* Process lines from an input file having the indicated
* name.
*/
#ifdef DEBUG
fprintf( stderr, "File name: %s\n", fname );
#endif
if( (Ifile_name = fname) && !(Ifile = fopen(fname, "r")) )
fprintf(stderr, "more: can't open %s\n", fname );
else
{
Flen = filelength( fileno(Ifile) );
fseek( Ifile, Start_here, 0 );
CLEAR_STACK();
docmd( ' ', 0 ); /* dump the first page */
for( ; ; )
{
for( ; ; )
{
if( docmd( getcmd(), 0 ) )
return;
if( feof( Ifile ) )
break;
}
E("\n\020\020\020 LAST LINE IN FILE \021\021\021");
if( docmd( getcmd(), 1 ) )
break;
}
fclose( Ifile );
}
}
/*--------------------------------------------------------------------------*/
main( int argc, char **argv )
{
#ifdef DEBUG
fprintf( stderr, "%s %s", *argv, argv[1] );
#endif
if( argc > 1 )
{
if( argv[1][0] == '-' )
usage();
else if( argv[1][0] == '+' )
{
Start_here = atol( &argv[1][1] );
printf("Starting at character %ld\n", Start_here );
push( Start_here );
++argv;
--argc;
}
}
if( argc <= 1 )
dofile( NULL );
else
for( ; --argc > 0 ; dofile(*(++argv)) )
;
exit(0);
}